package hyl.ext.web.ms;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import hyl.base.cache2.ExToken;
import hyl.core.Amy;
import hyl.core.MyFun;
import hyl.ext.base.MySession;
import hyl.ext.base.Response;
import hyl.ext.base.SessionFactory;

/**
 * 相当于单点登录系统的 资源系统或子系统
 * 
 * @author 37798955@qq.com
 *
 */

@WebServlet(displayName = "ssob", // 描述
		urlPatterns = { "/ssob/*" } // url
// ,loadOnStartup = 1 // 启动项
)
public class SsoB extends HttpServlet {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	// HttpServletResponse res
	public SsoB() {
		// System.out.println("SsoB()");
	}

//	@Override
//	public void init(ServletConfig config) {
//		if (_mb == null) {
//			_mb = TMsB.getInstance();
//			_mb.init();
//		}
//		System.out.println("SsoB.init()");
//	}

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse res) {
		// 根据 虚拟目录 切换处理函数
		String 虚拟目录 = req.getPathInfo();
		//MyFun.print(虚拟目录);
		if ("/a_b".equals(虚拟目录)) {
			f2_从A跳转到B(req, res);
		} else if ("/b_a".equals(虚拟目录)) {
			f1_从B跳转到A(req, res);
		} else if ("/b_a_s".equals(虚拟目录)) {
			f1_B通过A授权访问B资源(req, res);
		} else if ("/checkbuser".equals(虚拟目录)) {
			f1_验证账号(req, res);
		} else if ("/getbtoken".equals(虚拟目录)) {
			f1_获取令牌(req, res);
		}
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse res) {
		// 根据 虚拟目录 切换处理函数
		doGet(req, res);
	}

	public static void f1_获取令牌(HttpServletRequest req, HttpServletResponse res) {
		TMsB _mb = TMsB.getInstance();
		// BInfo bInfo = _mb._binfo;
		// String appidB = bInfo.getAppid();
		ExToken et = _mb.createExToken(2000);// 创建一个两秒的令牌
		try {
			PrintWriter out = res.getWriter();
			out.write(et.getId());
		} catch (IOException e) {
		}
	}

	public static void f1_验证账号(HttpServletRequest req, HttpServletResponse res) {
		String mw = req.getParameter("mw");
		// String btoken = req.getParameter("btoken");
		TMsB _mb = TMsB.getInstance();
		BInfo bInfo = _mb._binfo;
		Map<String, Object> mp = null;
		// if (_mb.contain1ExToken(btoken)) {
		String qString = bInfo.aes解密64(mw);
		String[] ss = qString.split(",");
		String buser = ss[0];
		String bpass = ss[1];
		Object[] params = { buser, buser, bpass };
		String sql = "select id,uname from user where (uname=? or uphone=? ) and upass=?";
		mp = Amy.getDB1().queryMap(sql, params);
		// }
		try {
			PrintWriter out = res.getWriter();
			if (mp == null || mp.isEmpty())
				out.write("N无此用户");
			else {
				// 验证成功返回用户id
				String str = MyFun.join(mp.get("id"), ",", mp.get("uname"));
				str = bInfo.aes加密64(str);
				// MyFun.print(str);
				out.write('Y' + str);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * b_a
	 * 1)创建令牌 
	 * 
	 * 2)加密用户信息
	 * 
	 * 3)定向到sso指定页 /sso/b_a.jsp
	 * @param req
	 * @param res
	 */
	public static void f1_从B跳转到A(HttpServletRequest req, HttpServletResponse res) {

		TMsB _mb = TMsB.getInstance();
		MySession sessionb = SessionFactory.getSession(req, res);
		BInfo bInfo = _mb._binfo;
		String appidB = bInfo.getAppid();
		ExToken et = _mb.createExToken();
		String tempkey = et.getId();
		String 明文 = MyFun.join2(",", appidB, sessionb.userIdToStr(), sessionb.getUname(), sessionb.getTokenId(),
				tempkey, sessionb.getIp());
		String mm = bInfo.aes加密64(明文);
		// MyFun.println( 明文, mm, tempkey);
		String aUrl = bInfo.getAURL();
		String url = MyFun.join(aUrl, "/sso/b_a.jsp?app=", appidB, "&mm=", mm);
		// MyFun.print(url);
		Response.direct(res, url);
	}

	/**
	 * 
	 * b_a_s
	 * 
	 * 
	 * 能不能实现一个会话 多次使用
	 * 
	 * 理论上可以.但是 必须确保会话不会泄露,这就增加了后续开发难度, 所以先砍掉这个需求
	 * 
	 * 也就是目前先实现 必须登录才能访问
	 * 
	 * @param res
	 * @param b资源页url
	 */
	public static void f1_B通过A授权访问B资源(HttpServletRequest req, HttpServletResponse res) {
		// MySession sessionb = SessionFactory.getSession(req, res);
		TMsB _mb = TMsB.getInstance();
		String b资源页url = req.getParameter("soureurl");
		String b回退页url = req.getParameter("returnurl");
		BInfo bInfo = _mb._binfo;
		String appidB = bInfo.getAppid();
		int m = 60000;// 可修改 1分钟必须登录
		ExToken exToken = _mb.createExToken(m);// 一个n分钟的令牌
		String 明文 = MyFun.join2(",", appidB, exToken.getId(), exToken.getStampstr(), b资源页url, b回退页url);
		String mm = bInfo.aes加密64(明文);
		MyFun.println(明文, mm, exToken.getStampstr(), b资源页url, b回退页url);
		String aUrl = bInfo.get("AURL");
		String url = MyFun.join(aUrl, "/sso/a/b_a_s?app=", appidB, "&chk=", exToken.getStampstr(), "&mm=", mm);
		Response.direct(res, url);
	}

	/**
	 * 从 a 跳转到 b
	 * 
	 * 先验证签名再解码 ,否则每个都解码 遇到暴力攻击非常麻烦
	 * 
	 * @param req
	 * @param res
	 */
	public static void f2_从A跳转到B(HttpServletRequest req, HttpServletResponse res) {
		/*
		 * 如果令牌不对,说明密文不对 阻止登录
		 */
		TMsB _mb = TMsB.getInstance();
		BInfo bInfo = _mb._binfo;

		String 密文 = req.getParameter("mw");
		// 先验证签名再解码 ,否则每个都解码 遇到暴力攻击非常麻烦
		String 明文 = bInfo.aes解密64(密文);
		// MyFun.print(明文);
		String[] mms = 明文.split(",");
		String appid_b = mms[0];
		String session_b = mms[1];
		String tokenb = mms[2];
		//MyFun.print(appid_b, session_b, tokenb, bInfo.getB首页());
		// 如果 appid 与本地不一致 阻止登录
		if (MyFun.isBlank(appid_b) || (!appid_b.equals(bInfo.getAppid()))) {
			req.setAttribute("code", 40);
			req.setAttribute("msg", "appid不正确");
			Response.direct(res, bInfo.getB首页());
			return;
		}

		// System.out.println("-----2-----");
		if (MyFun.isBlank(tokenb)||!_mb.contain1ExToken(tokenb)) {
			// 如果令牌不对,说明密文不对 阻止登录
			req.setAttribute("code", 42);
			req.setAttribute("msg", "令牌异常,非法登录");
			Response.direct(res, bInfo.getB首页());
			return;
		}
		// System.out.println("-----3-----");

		MySession msession = SessionFactory.getSessionById(session_b);
		if (msession == null) {
			req.setAttribute("code", 43);
			req.setAttribute("msg", "会话过期,需要重新登录");
			Response.direct(res, bInfo.getB首页());
			return;
		}
		// 添加到cookie 和
		Response.addCookie(res, SessionFactory.SessionKey, msession.getTokenId(),
				SessionFactory.EFFECTTIMES.intValue());
		SessionFactory.putSession(req, msession);
		// 传过来的时候session 已经 获取或者重建,肯定没有问题,如果有问题 一定是哪里错了
		Response.direct(res, bInfo.getB首页());

		//System.out.println("-----5-----");
	}

}
